// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_CAST_NET_RTCP_RTCP_UTILITY_H_
#define MEDIA_CAST_NET_RTCP_RTCP_UTILITY_H_

#include <stddef.h>
#include <stdint.h>

#include "base/big_endian.h"
#include "base/macros.h"
#include "media/cast/logging/logging_defines.h"
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/net/rtcp/rtcp_defines.h"

namespace media {
namespace cast {

    // RFC 3550 page 44, including end null.
    static const size_t kRtcpCnameSize = 256;

    static const uint32_t kCast = ('C' << 24) + ('A' << 16) + ('S' << 8) + 'T';

    static const uint32_t kCst2 = ('C' << 24) + ('S' << 16) + ('T' << 8) + '2';

    static const uint8_t kReceiverLogSubtype = 2;

    static const size_t kRtcpMaxReceiverLogMessages = 256;
    static const size_t kRtcpMaxCastLossFields = 100;

    struct RtcpCommonHeader {
        uint8_t V; // Version.
        bool P; // Padding.
        uint8_t IC; // Item count / subtype.
        uint8_t PT; // Packet Type.
        size_t length_in_octets;
    };

    class RtcpParser {
    public:
        RtcpParser(uint32_t local_ssrc, uint32_t remote_ssrc);
        ~RtcpParser();

        // Gets/Sets the ID of the latest frame that could possibly be ACK'ed.  This
        // is used when expanding truncated frame IDs during Parse().  This only needs
        // to be called if the client uses cast_message().
        FrameId max_valid_frame_id() const { return max_valid_frame_id_; }
        void SetMaxValidFrameId(FrameId max_valid_frame_id);

        // Parse the RTCP packet.
        bool Parse(base::BigEndianReader* reader);

        bool has_sender_report() const { return has_sender_report_; }
        const RtcpSenderInfo& sender_report() const
        {
            return sender_report_;
        }

        bool has_last_report() const { return has_last_report_; }
        uint32_t last_report() const { return last_report_; }
        uint32_t delay_since_last_report() const { return delay_since_last_report_; }

        bool has_receiver_log() const { return !receiver_log_.empty(); }
        const RtcpReceiverLogMessage& receiver_log() const { return receiver_log_; }
        RtcpReceiverLogMessage* mutable_receiver_log() { return &receiver_log_; }

        bool has_cast_message() const { return has_cast_message_; }
        const RtcpCastMessage& cast_message() const { return cast_message_; }
        RtcpCastMessage* mutable_cast_message() { return &cast_message_; }
        // Return if successfully parsed the extended feedback.
        bool has_cst2_message() const { return has_cst2_message_; }

        bool has_receiver_reference_time_report() const
        {
            return has_receiver_reference_time_report_;
        }
        const RtcpReceiverReferenceTimeReport&
        receiver_reference_time_report() const
        {
            return receiver_reference_time_report_;
        }

        bool has_picture_loss_indicator() const
        {
            return has_picture_loss_indicator_;
        }

    private:
        bool ParseCommonHeader(base::BigEndianReader* reader,
            RtcpCommonHeader* parsed_header);
        bool ParseSR(base::BigEndianReader* reader, const RtcpCommonHeader& header);
        bool ParseRR(base::BigEndianReader* reader, const RtcpCommonHeader& header);
        bool ParseReportBlock(base::BigEndianReader* reader);
        bool ParsePli(base::BigEndianReader* reader, const RtcpCommonHeader& header);
        bool ParseApplicationDefined(base::BigEndianReader* reader,
            const RtcpCommonHeader& header);
        bool ParseCastReceiverLogFrameItem(base::BigEndianReader* reader);
        bool ParseFeedbackCommon(base::BigEndianReader* reader,
            const RtcpCommonHeader& header);
        bool ParseExtendedReport(base::BigEndianReader* reader,
            const RtcpCommonHeader& header);
        bool ParseExtendedReportReceiverReferenceTimeReport(
            base::BigEndianReader* reader,
            uint32_t remote_ssrc);
        bool ParseExtendedReportDelaySinceLastReceiverReport(
            base::BigEndianReader* reader);

        const uint32_t local_ssrc_;
        const uint32_t remote_ssrc_;

        bool has_sender_report_;
        RtcpSenderInfo sender_report_;

        uint32_t last_report_;
        uint32_t delay_since_last_report_;
        bool has_last_report_;

        // |receiver_log_| is a vector vector, no need for has_*.
        RtcpReceiverLogMessage receiver_log_;

        bool has_cast_message_;
        RtcpCastMessage cast_message_;
        bool has_cst2_message_;

        bool has_receiver_reference_time_report_;
        RtcpReceiverReferenceTimeReport receiver_reference_time_report_;

        // Tracks recently-parsed RTP timestamps so that the truncated values can be
        // re-expanded into full-form.
        RtpTimeTicks last_parsed_sr_rtp_timestamp_;
        RtpTimeTicks last_parsed_frame_log_rtp_timestamp_;

        // The maximum possible re-expanded frame ID value.
        FrameId max_valid_frame_id_;

        // Indicates if sender received the Pli message from the receiver.
        bool has_picture_loss_indicator_;

        DISALLOW_COPY_AND_ASSIGN(RtcpParser);
    };

    // Converts a log event type to an integer value.
    // NOTE: We have only allocated 4 bits to represent the type of event over the
    // wire. Therefore, this function can only return values from 0 to 15.
    uint8_t ConvertEventTypeToWireFormat(CastLoggingEvent event);

    // The inverse of |ConvertEventTypeToWireFormat()|.
    CastLoggingEvent TranslateToLogEventFromWireFormat(uint8_t event);

    // Splits an NTP timestamp having a microsecond timebase into the standard two
    // 32-bit integer wire format.
    void ConvertTimeToFractions(int64_t ntp_time_us,
        uint32_t* seconds,
        uint32_t* fractions);

    // Maps a base::TimeTicks value to an NTP timestamp comprised of two components.
    void ConvertTimeTicksToNtp(const base::TimeTicks& time,
        uint32_t* ntp_seconds,
        uint32_t* ntp_fractions);

    // Create a NTP diff from seconds and fractions of seconds; delay_fraction is
    // fractions of a second where 0x80000000 is half a second.
    uint32_t ConvertToNtpDiff(uint32_t delay_seconds, uint32_t delay_fraction);

    // Maps an NTP timestamp, comprised of two components, to a base::TimeTicks
    // value.
    base::TimeTicks ConvertNtpToTimeTicks(uint32_t ntp_seconds,
        uint32_t ntp_fractions);

    bool IsRtcpPacket(const uint8_t* packet, size_t length);

    uint32_t GetSsrcOfSender(const uint8_t* rtcp_buffer, size_t length);

} // namespace cast
} // namespace media

#endif // MEDIA_CAST_NET_RTCP_RTCP_UTILITY_H_
